﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace _01_OOP
{
    /// <summary>
    /// 实列化
    /// </summary>
    internal class InstanceExample
    {
        public class Student
        {
            public int Id { get; set; }
            public Student()
            {
                Id = -1;
                Console.WriteLine("(void)构造被调用");
            }
            public Student(int id)
            {
                Id = id;
                Console.WriteLine("(int id)构造被调用");
            }
        }

        public static void Test()
        {
            var student = Create4<Student>();
        }

        /// <summary>
        /// 方式1：new
        /// </summary>
        /// <returns></returns>
        public static Student Create1()
        {
            Student student = new Student();
            return student;
        }
        /// <summary>
        /// 方式二：泛型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T Create2<T>()
            where T : class, new()
        {
            T student = new T();
            return student;
        }
        /// <summary>
        /// 方式三：反射
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T Create3<T>()
        {
            return Activator.CreateInstance<T>();
        }
        private static Delegate? lambdaFactory = null;
        /// <summary>
        /// 方式四：表达式树
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        /// <exception cref="NullReferenceException"></exception>
        public static T Create4<T>()
        {
            if (lambdaFactory is null)
            {
                var constructor = typeof(T).GetConstructor(Type.EmptyTypes);
                if (constructor == null)
                {
                    throw new NullReferenceException(nameof(constructor));
                }
                //body:new Student();
                var newExpression = Expression.New(constructor);
                //lambda:()=>new Student();
                var lambda = Expression.Lambda(newExpression);

                //编译：注意编译性能特别差，应该吧编译的结果保存下来。
                lambdaFactory = lambda.Compile();
            }
            //DynamicInvoke：注意动态执行性能特别差
            //lambdaFactory.DynamicInvoke();
            var factory = lambdaFactory as Func<T>;
            if (factory == null)
            {
                throw new NullReferenceException(nameof(factory));
            }
            return factory.Invoke();
        }
        private static Delegate? emitFactory = null;
        /// <summary>
        /// 方式五：emit
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        /// <exception cref="NullReferenceException"></exception>
        public static T Create5<T>()
        {
            if (emitFactory == null)
            {
                var dynamicMethod = new DynamicMethod($"DynamicCreate{typeof(T).Name}", typeof(T), Type.EmptyTypes);
                //获取il生成器
                var generator = dynamicMethod.GetILGenerator();
                var constructor = typeof(T).GetConstructor(Type.EmptyTypes);
                if (constructor == null)
                {
                    throw new NullReferenceException(nameof(constructor));
                }
                var local0 = generator.DeclareLocal(typeof(T));
                generator.Emit(OpCodes.Nop);
                generator.Emit(OpCodes.Newobj, constructor);
                generator.Emit(OpCodes.Stloc, local0);
                generator.Emit(OpCodes.Ldloc, local0);
                generator.Emit(OpCodes.Ret);
                emitFactory = dynamicMethod.CreateDelegate<Func<T>>();
            }
            var factory = emitFactory as Func<T>;
            if (factory == null)
            {
                throw new NullReferenceException(nameof(factory));
            }
            return factory.Invoke();
        }
    }
}
